package com.jf.nep.scene;

import java.util.ArrayList;
import java.util.List;

import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

import com.jf.nep.MathUtil;
import com.jf.nep.render.Mesh;
import com.jf.nep.render.MeshInstance;
import com.jf.nep.render.MeshRenderer;
import com.jf.nep.scene.object.FloorTile;
import com.jf.nep.shader.ShaderStoreImpl;

public class SceneRenderer {
    private MeshRenderer renderer;
    private boolean quit;
    private Mesh floor;
    private ShaderStoreImpl shaderStore;
    private Matrix4f directionMatrix;
    
    private List<MeshInstance> meshInstances = new ArrayList<MeshInstance>();
    private Vector3f globalAngle;
    
    private float posDelta = 3.0f;
    private long time;
    private float dt;
    private long lastTime;


    public SceneRenderer(boolean fullScreen) {
        renderer = new MeshRenderer(fullScreen);
    }

    public void create() throws Exception {
        renderer.create();
        shaderStore = new ShaderStoreImpl();
        shaderStore.create();
        createMeshes();
        renderer.setShaderStore(shaderStore);
        renderer.getMeshStore().createMesh(floor);
        Mouse.setGrabbed(true);
    }
    
    
    public void movePos(Vector3f vector, float delta, float useX, float useZ) {
        Vector4f forward = new Vector4f(useX, 0, useZ, 1);            
        Matrix4f.transform(directionMatrix, forward, forward);
        vector.x -= delta * forward.x * dt;
        vector.z += delta * forward.z * dt;
    }
    
    private void createMeshes() throws Exception {
        floor = new FloorTile();
        globalAngle = new Vector3f(0,30,30);
        
        for (int x = -9; x <= 9; x++) {
            for (int z = -4; z < 0; z++) {
                MeshInstanceImpl floorInstance = new MeshInstanceImpl();
                floorInstance.setMeshId(floor.id());
                floorInstance.setProgramId(floor.id()+"/"+x+"/"+z);
                floorInstance.rotateScaleAndTranslate(new Vector3f(0,0,x*10), new Vector3f(0.2f,1,0.2f), new Vector3f(x/2.0f,Math.abs(x/7.0f),z));
                meshInstances.add(floorInstance);
                shaderStore.createShaderProgram(floorInstance.getProgramId());
            }
        }
    }
    
    public void destroy() {
        Mouse.setGrabbed(false);
        renderer.getMeshStore().destroyAll();
        shaderStore.destroyShaders();        
        renderer.destroy();
    }
        
    public boolean process() throws Exception {
        processKeyboardAndMouse(); 
        if (quit) {
            return false;
        }
        
        time = Sys.getTime();
        dt = (time - lastTime) / 1000.0f;
        lastTime = time;
        
        globalAngle.z += 0.2f;
        if (globalAngle.z > 360f) {
            globalAngle.z -= 360f;
        }
        
        /*globalAngle.y += 0.025f;
        if (globalAngle.y > 360f) {
            globalAngle.y -= 360f;
        }*/
        
        /*for (MeshInstance meshInstance : meshInstances) {
            ((MeshInstanceImpl)meshInstance).rotate(globalAngle);
        }*/
        
        renderer.setLightAngle(globalAngle);
        
        return renderer.process(meshInstances);
    }
    
    
    private void processKeyboardAndMouse() {
        if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
            quit = true;
            return;
        }
        float dx = Mouse.getDX();
        float dy = Mouse.getDY();
        
        Vector3f cameraAngle = renderer.getCameraAngle();        
        
        cameraAngle.y += dx * 0.1f;
        cameraAngle.x -= dy * 0.1f;
        if (cameraAngle.x > 45f) {
            cameraAngle.x = 45f;
        }
        if (cameraAngle.x < -45f) {
            cameraAngle.x = -45f;
        }
        renderer.setCameraAngle(cameraAngle);        
        
        directionMatrix = new Matrix4f();
        Matrix4f.rotate(MathUtil.degreesToRadians(cameraAngle.y), new Vector3f(0,1,0), directionMatrix, directionMatrix);
        
        Vector3f cameraPos = renderer.getCameraPos();
        if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
            movePos(cameraPos, posDelta, -1, 0);
        } 
        if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
            movePos(cameraPos, posDelta, 1, 0);
        } 
        if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
            movePos(cameraPos, posDelta, 0, 1);
        } 
        if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
            movePos(cameraPos, posDelta, 0, -1);
        } 
        renderer.setCameraPos(cameraPos);        
    }

}
